﻿#region Header

/*
Remote views based on linq
By

Pascal Hauser 
Dipl. Ing. in Informatik, Hochschule für Technik Rapperswil, 2006
Master Thesis, Hochschule für Technik Rapperswil, 2008-2010

and

Raphael Gfeller
Dipl. Ing. in Informatik, Hochschule für Technik Rapperswil, 2006
Master Thesis, Hochschule für Technik Rapperswil, 2008-2010

*/

#endregion

#region Usings

using System;
using System.Collections.Generic;
using System.Threading;
using Sebarf.Services.Interfaces;
using Sebastof.Services.Interfaces;

#endregion

namespace Sebastof.Services.Impl {
	/// <summary>
	/// Default implementation of and ObjectMapping service, implementing the IObjectMappingLocatorService and the
	/// IObjectMappingRegisterService interface.
	/// It provides resolving of an object based on its id in vice versa.
	/// NOTE: Object to ID mapping for ApplicationObject implementations
	/// is not possible through this mapper as the references are not stored as this would
	/// result in memory consumption problems as objects would never be collected by the 
	/// garbage collector.
	/// </summary>
	public class ObjectMappingLocatorRegisterService : Service, IObjectMappingLocatorService, IObjectMappingRegisterService {
		private const int CLEANINTERVAL = 500;

		/// <summary>
		/// the id to object lookup table
		/// </summary>
		private readonly Dictionary<string, WeakReference> IdToObjectMap = new Dictionary<string, WeakReference>();

		/// <summary>
		/// the object to id lookup table
		/// </summary>
		private readonly Dictionary<object, string> ObjectToIdMap = new Dictionary<object, string>();

		private int AccessCount;

		#region IObjectMappingLocatorService Members

		/// <summary>
		/// property for accessing the object to id lookup table
		/// </summary>
		/// <returns></returns>
		public Dictionary<object, string> GetObjectToIdMappingTable() {
			return ObjectToIdMap;
		}

		/// <summary>
		/// property for accessing the id to object lookup table
		/// </summary>
		/// <returns></returns>
		public Dictionary<string, WeakReference> GetIdToObjectMappingTable() {
			Clean();
			return IdToObjectMap;
		}

		#endregion

		#region IObjectMappingRegisterService Members

		/// <summary>
		/// adds an object to the mapper. Object to ID mapping for ApplicationObject implementations
		/// is not possible through this mapper as the references are not stored as this would
		/// result in memory consumption problems as objects would never be collected by the 
		/// garbage collector.
		/// </summary>
		/// <param name="id"></param>
		/// <param name="obj"></param>
		public void AddMapping(string id, object obj) {
			Monitor.Enter(this);
			Clean();
			if (!IdToObjectMap.ContainsKey(id) && !ObjectToIdMap.ContainsKey(obj)) {
				IdToObjectMap.Add(id, new WeakReference(obj));
				if (!(obj is ISebastofApplicationObject)) {
					ObjectToIdMap.Add(obj, id);
				}
			}
			Monitor.Exit(this);
		}

		#endregion

		private void Clean() {
			AccessCount++;
			if (AccessCount > CLEANINTERVAL) {
				lock (this) {
					if (AccessCount > CLEANINTERVAL) {
						var keysToRemove = new List<string>();
						foreach (string k in IdToObjectMap.Keys) {
							if (!IdToObjectMap[k].IsAlive) {
								keysToRemove.Add(k);
							}
						}
						foreach (string k in keysToRemove) {
							IdToObjectMap.Remove(k);
						}
						AccessCount = 0;
					}
				}
			}
		}

		/// <summary>
		/// returns the statistics for this service
		/// </summary>
		/// <param name="statisticData"></param>
		/// <returns></returns>
		protected override ServiceStatistics OnCollectStatisticData(ServiceStatistics statisticData) {
			statisticData.AddEntry("IdToObjectEntryCount", (double)IdToObjectMap.Count);
			statisticData.AddEntry("ObjectToIdEntryCount", (double)ObjectToIdMap.Count);
			return statisticData;
		}
	}
}